package com.googlecode.skald.client.affordances;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import com.google.gwt.user.client.ui.MenuBar;
import com.googlecode.skald.client.cmds.AffordanceCommand;
import com.googlecode.skald.client.cmds.DisplayedSendCmd;

/**
 * Utility methods related to Affordance objects. 
 * 
 * @author Zach Tomaszewski
 */
public class Affordances {

  //private static Logger logger = Logger.getLogger(Affordances.class.getName());
  
  /**
   * Returns only those affordances from the given list that contain the 
   * obj string in any element position.  
   * 
   * @param affords  The affordances to filter through
   * @param obj      The string to fully match in any of each affordance's 
   *                 {@link Affordance#getDisplayElements()}
   * @return  A new list of those matching affordances (which may be empty).
   */
  public static List<Affordance> forObject(List<Affordance> affords, String obj) {
    List<Affordance> found = new ArrayList<Affordance>();
    for (Affordance a : affords) {
      for (String element : a.getDisplayElements()) {
        if (obj.equals(element)) {
          found.add(a);
          break;  //on to next affordance
        }
      }
    }
    return found;
  }
  
  /**
   * Returns a menu of the of the given affordances using default settings.
   * Specifically, the menu is constructed using all {@link MenuOption}s
   * and each affordance invokes a {@link DisplayedSendCmd}. 
   * 
   * @see #getMenu(List, Class)
   */
  public static MenuBar getMenu(List<? extends Affordance> affordances) {
    return getMenu(affordances, 
                   EnumSet.allOf(MenuOption.class), 
                   new DisplayedSendCmd(null));
  }
  
  /**
   * Returns a menu using the given {@link MenuOption}s.
   * Each affordance invokes a {@link DisplayedSendCmd}. 
   * 
   * @see #getMenu(List, EnumSet, AffordanceCommand)
   */
  public static MenuBar getMenu(List<? extends Affordance> affordances,
                                EnumSet<MenuOption> opts) {
    return getMenu(affordances, opts, new DisplayedSendCmd(null));
  }
  
  /** 
   * Returns a menu of the given affordances.  Each affordance in the menu
   * will be associated with a new instance of the given Command class.  The
   * class must have a constructor that takes a single String, which will be 
   * passed {@link Affordance#getCommandText()}.  When an affordance is 
   * selected from the menu, the corresponding command will be invoked.
   * <p>
   * In constructing the menu, this method expands each affordance in the 
   * given list into its display elements.  It may then collapse those menus
   * in various ways according to the given {@link MenuOption}s.
   */
  public static MenuBar getMenu(List<? extends Affordance> affordances,
                                EnumSet<MenuOption> opts, 
                                AffordanceCommand builder) {
    TrieNode trie = buildTrie(affordances);
    trie.collapse(opts);
    return TrieNode.toMenuBar(trie, builder);
  }

  /** 
   * Builds a sorted trie structure from the given affordances. 
   */
  protected static TrieNode buildTrie(List<? extends Affordance> affordances) {
    //get sorted and drop dups at the same time
    SortedSet<Affordance> set = new TreeSet<Affordance>(affordances);
    TrieNode root = new TrieNode(null);
    for (Affordance a : set) {
      TrieNode curr = root;
      String[] elems = a.getDisplayElements();
      for (int i = 0; i < elems.length; i++) {
        String elem = elems[i]; 
        TrieNode child = new TrieNode(elem, a.isWeak());
        curr.children.add(child);
        curr = child;
        if (i == elems.length - 1) {
          //end of path of elements, so insert command here
          curr.cmd = a.getCommandText();
        }
      }
    }    
    return root;
  }
    
}
